home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 3
/
Cream of the Crop 3.iso
/
comm
/
wnos5src.zip
/
TCPUSER.C
< prev
next >
Wrap
Text File
|
1993-08-09
|
8KB
|
347 lines
/* User calls to TCP */
#include <stdio.h>
#include "global.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "socket.h"
#include "internet.h"
#include "tcp.h"
#include "ip.h"
#include "icmp.h"
#include "proc.h"
int16 Tcp_window = DEF_WND;
struct tcb *
open_tcp(
struct socket *lsocket, /* Local socket */
struct socket *fsocket, /* Remote socket */
int mode, /* Active/passive/server */
int16 window, /* Receive window (and send buffer) sizes */
void (*r_upcall)(), /* Function to call when data arrives */
void (*t_upcall)(), /* Function to call when ok to send more data */
void (*s_upcall)(), /* Function to call when connection state changes */
int tos,
int user) /* User linkage area */
{
struct connection conn;
struct tcb *tcb;
if(lsocket == NULLSOCK){
return NULLTCB;
}
conn.local.address = lsocket->address;
conn.local.port = lsocket->port;
if(fsocket != NULLSOCK){
conn.remote.address = fsocket->address;
conn.remote.port = fsocket->port;
} else {
conn.remote.address = 0;
conn.remote.port = 0;
}
if((tcb = lookup_tcb(&conn)) == NULLTCB) {
if((tcb = create_tcb(&conn)) == NULLTCB) {
return NULLTCB;
}
} else if(tcb->state != TCP_LISTEN) {
return NULLTCB;
}
tcb->user = user;
if(window != 0) {
tcb->window = tcb->rcv.wnd = window;
} else {
tcb->window = tcb->rcv.wnd = Tcp_window;
}
tcb->snd.wnd = 1; /* Allow space for sending a SYN */
tcb->r_upcall = r_upcall;
tcb->t_upcall = t_upcall;
tcb->s_upcall = s_upcall;
tcb->tos = tos;
switch(mode){
case TCP_SERVER:
tcb->flags.clone = 1;
case TCP_PASSIVE: /* Note fall-thru */
setstate(tcb,TCP_LISTEN);
break;
case TCP_ACTIVE:
/* Send SYN, go into TCP_SYN_SENT state */
tcb->flags.active = 1;
send_syn(tcb);
setstate(tcb,TCP_SYN_SENT);
tcp_output(tcb);
break;
}
return tcb;
}
/* User send routine */
int
send_tcp(struct tcb *tcb,struct mbuf *bp)
{
int16 cnt = len_p(bp);
if(tcb == NULLTCB || bp == NULLBUF) {
if(bp) {
free_p(bp);
}
return -1;
}
switch(tcb->state){
case TCP_CLOSED:
free_p(bp);
return -1;
case TCP_LISTEN:
if(tcb->conn.remote.address == 0 && tcb->conn.remote.port == 0) {
/* Save data for later */
append(&tcb->sndq,bp);
tcb->sndcnt += cnt;
break;
}
/* Change state from passive to active */
tcb->flags.active = 1;
send_syn(tcb);
setstate(tcb,TCP_SYN_SENT); /* Note fall-thru */
case TCP_SYN_SENT:
case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
case TCP_CLOSE_WAIT:
append(&tcb->sndq,bp);
tcb->sndcnt += cnt;
tcp_output(tcb);
break;
case TCP_FINWAIT1:
case TCP_FINWAIT2:
case TCP_CLOSING:
case TCP_LAST_ACK:
case TCP_TIME_WAIT:
free_p(bp);
return -1;
}
return (int)cnt;
}
/* User receive routine */
int
recv_tcp(struct tcb *tcb,struct mbuf **bpp,int16 cnt)
{
if(tcb == NULLTCB || bpp == NULLBUFP) {
return -1;
}
if(tcb->rcvcnt == 0) {
/* If there's nothing on the queue, our action depends on what state
* we're in (i.e., whether or not we're expecting any more data).
* If no more data is expected, then simply return 0; this is
* interpreted as "end of file". Otherwise return -1.
*/
switch(tcb->state) {
case TCP_LISTEN:
case TCP_SYN_SENT:
case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
case TCP_FINWAIT1:
case TCP_FINWAIT2:
return -1;
case TCP_CLOSED:
case TCP_CLOSE_WAIT:
case TCP_CLOSING:
case TCP_LAST_ACK:
case TCP_TIME_WAIT:
*bpp = NULLBUF;
return 0;
}
}
/* cnt == 0 means "I want it all" */
if(cnt == 0) {
cnt = tcb->rcvcnt;
}
/* See if the user can take all of it */
if(tcb->rcvcnt <= cnt) {
cnt = tcb->rcvcnt;
*bpp = tcb->rcvq;
tcb->rcvq = NULLBUF;
} else {
*bpp = alloc_mbuf(cnt);
pullup(&tcb->rcvq,(*bpp)->data,cnt);
(*bpp)->cnt = cnt;
}
tcb->rcvcnt -= cnt;
tcb->rcv.wnd += cnt;
/* Do a window update if it was closed */
if(cnt == tcb->rcv.wnd) {
tcb->flags.force = 1;
tcp_output(tcb);
}
return (int)cnt;
}
/* This really means "I have no more data to send". It only closes the
* connection in one direction, and we can continue to receive data
* indefinitely.
*/
int
close_tcp(struct tcb *tcb)
{
if(tcpval(tcb)) {
switch(tcb->state){
case TCP_LISTEN:
case TCP_SYN_SENT:
close_self(tcb,NORMAL);
case TCP_CLOSED:
return 0; /* Unlikely */
case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
case TCP_CLOSE_WAIT:
tcb->sndcnt++;
tcb->snd.nxt++;
setstate(tcb,tcb->state == TCP_CLOSE_WAIT ? TCP_LAST_ACK : TCP_FINWAIT1);
tcp_output(tcb);
return 0;
case TCP_FINWAIT1:
case TCP_FINWAIT2:
case TCP_CLOSING:
case TCP_LAST_ACK:
case TCP_TIME_WAIT:
return -1;
}
}
return -1; /* "Can't happen" */
}
/* Delete TCB, free resources. The user is not notified, even if the TCB is
* not in the TCP_CLOSED state. This function should normally be called by the
* user only in response to a state change upcall to TCP_CLOSED state.
*/
int
del_tcp(struct tcb *conn)
{
struct tcb *tcb, *tcblast = NULLTCB;
struct reseq *rp, *rp1;
for(tcb = Tcbs; tcb != NULLTCB; tcblast = tcb, tcb = tcb->next) {
if(tcb == conn) {
break;
}
}
if(tcb == NULLTCB) {
return -1; /* conn was NULL, or not on list */
}
if(tcblast != NULLTCB) {
tcblast->next = tcb->next;
} else {
Tcbs = tcb->next; /* was first on list */
}
stop_timer(&tcb->timer);
for(rp = tcb->reseq; rp != NULLRESEQ; rp = rp1) {
rp1 = rp->next;
free_p(rp->bp);
xfree(rp);
}
tcb->reseq = NULLRESEQ;
free_p(tcb->rcvq);
free_p(tcb->sndq);
xfree(tcb);
return 0;
}
/* Return 1 if arg is a valid TCB, 0 otherwise */
int
tcpval(struct tcb *tcb)
{
if(tcb != NULLTCB) {
struct tcb *tcb1;
for(tcb1 = Tcbs; tcb1 != NULLTCB; tcb1 = tcb1->next) {
if(tcb1 == tcb) {
return 1;
}
}
}
return 0;
}
/* Kick a particular TCP connection */
int
kick_tcp(struct tcb *tcb)
{
if(!tcpval(tcb)) {
return -1;
}
tcb->flags.force = 1;
tcp_timeout(tcb);
return 0;
}
/* Kick all TCP connections to specified address; return number kicked */
int
kick(int32 addr)
{
struct tcb *tcb;
int cnt = 0;
for(tcb = Tcbs; tcb != NULLTCB; tcb = tcb->next) {
if(tcb->conn.remote.address == addr) {
kick_tcp(tcb);
cnt++;
}
}
return cnt;
}
/* Clear all TCP connections */
void
reset_all(void)
{
struct tcb *tcb, *tcbtmp = NULLTCB;
for(tcb = Tcbs; tcb != NULLTCB; tcb = tcbtmp) {
int flag = 0;
tcbtmp = tcb->next;
if(tcb->conn.local.address == Ip_addr) {
flag = 1;
}
reset_tcp(tcb);
pwait(NULL); /* Let the RSTs go forth */
if(flag) {
tcbtmp = Tcbs;
}
}
}
void
reset_tcp(struct tcb *tcb)
{
if(tcb != NULLTCB) {
if(tcb->state != TCP_LISTEN && tcb->state != TCP_TIME_WAIT) {
struct tcp fakeseg;
struct ip fakeip;
/* Compose a fake segment with just enough info to generate the
* correct RST reply
*/
fakeseg.flags.rst = 0;
fakeseg.dest = tcb->conn.local.port;
fakeseg.source = tcb->conn.remote.port;
fakeseg.flags.ack = 1;
/* Here we try to pick a sequence number with the greatest likelihood
* of being in his receive window.
*/
fakeseg.ack = tcb->snd.nxt + tcb->snd.wnd - 1;
fakeip.dest = tcb->conn.local.address;
fakeip.source = tcb->conn.remote.address;
fakeip.tos = tcb->tos;
reset(&fakeip,&fakeseg);
}
close_self(tcb,RESET);
}
}